package com.yst.sqlbuilder;

import com.yst.lambda.LambdaFuncUtils;
import com.yst.lambda.TableFieldFunc;
import org.apache.commons.lang3.StringUtils;

import java.io.Serializable;
import java.util.Map;
import java.util.Objects;

public abstract class MathExp<T> implements IExpression {

    public enum MathType {

        SUM("sum"),
        COUNT("count"),
        AVG("avg"),
        MAX("max"),
        MIN("min"),
        ;

        public final String expression;
        MathType(String expression){
            this.expression=expression;
        }
    }

    private TableFieldFunc<T,?> field;
    private MathType mathType=MathType.SUM;


    private Map<String,String> fieldMap;
    public Map<String,String> getFieldMap(){
        return fieldMap;
    }
    public MathExp<T> setFieldMap(Map<String,String> fieldMap){
        this.fieldMap=fieldMap;
        return this;
    }
    public MathExp(MathType mathType,TableFieldFunc<T,?> field,Map<String,String> fieldMap){
        this.fieldMap=fieldMap;
        this.field=field;
        this.mathType=mathType;
    }

    public MathExp(MathType mathType,TableFieldFunc<T,?> field){
        this(mathType,field,null);
    }

    public abstract String generateExpression(GenerateParam param);

    private String alisa;
    public String getAlisa(){
        return alisa;
    }

    public MathExp<T> setAlisa(String alisa){
        this.alisa=alisa;
        return this;
    }

    private static String getRealFieldName(TableFieldFunc<?,?> field,Map<String,String> fieldMap){
        String fieldName=LambdaFuncUtils.getFieldName(field);
        if (Objects.nonNull(fieldMap)&&fieldMap.containsKey(fieldName)){
            fieldName=fieldMap.get(fieldName);
        }
        return fieldName;
    }
    public static <T> MathExp<T> sum(TableFieldFunc<T,?> field, Map<String,String> fieldMap){
        return new MathExp<T>(MathType.SUM, field) {
            @Override
            public String generateExpression(GenerateParam param) {
                String fieldName=MathExp.getRealFieldName(field,fieldMap);
                if (Objects.nonNull(getAlisa())&& StringUtils.isEmpty(getAlisa())) {
                    return String.format("sum(%s) %s", fieldName, getAlisa());
                }
                else
                {
                    return String.format("sum(%s) %s", fieldName, fieldName);
                }
            }
        }.setFieldMap(fieldMap);
    }

    public static <T> MathExp<T> sum(TableFieldFunc<T,?> field){
        return sum(field,null);
    }

    public static <T> MathExp<T> count(TableFieldFunc<T,?> field,Map<String,String> fieldMap){
        return new MathExp<T>(MathType.COUNT, field) {
            @Override
            public String generateExpression(GenerateParam param) {
                String fieldName=MathExp.getRealFieldName(field,this.getFieldMap());
                if (Objects.nonNull(getAlisa())&& !StringUtils.isEmpty(getAlisa())) {
                    return String.format("count(%s) %s", fieldName, getAlisa());
                }
                else
                {
                    return String.format("count(%s) %s", fieldName, fieldName);
                }
            }
        }.setFieldMap(fieldMap);
    }

    public static <T> MathExp<T> count(TableFieldFunc<T,?> field){
        return count(field,null);
    }

    public static <T> MathExp<T> avg(TableFieldFunc<T,?> field,Map<String,String> fieldMap){
        return new MathExp<T>(MathType.AVG, field) {
            @Override
            public String generateExpression(GenerateParam param) {
                String fieldName=MathExp.getRealFieldName(field,this.getFieldMap());
                if (Objects.nonNull(getAlisa())&& !StringUtils.isEmpty(getAlisa())) {
                    return String.format("avg(%s) %s", fieldName, getAlisa());
                }
                else
                {
                    return String.format("avg(%s) %s", fieldName, fieldName);
                }
            }
        }.setFieldMap(fieldMap);
    }

    public static <T> MathExp<T> avg(TableFieldFunc<T,?> field){
        return avg(field,null);
    }

    public static <T> MathExp<T> min(TableFieldFunc<T,?> field,Map<String,String> fieldMap){
        return new MathExp<T>(MathType.MIN, field) {
            @Override
            public String generateExpression(GenerateParam param) {
                String fieldName=MathExp.getRealFieldName(field,this.getFieldMap());
                if (Objects.nonNull(getAlisa())&& !StringUtils.isEmpty(getAlisa())) {
                    return String.format("min(%s) %s", fieldName, getAlisa());
                }
                else
                {
                    return String.format("min(%s) %s", fieldName, fieldName);
                }
            }
        }.setFieldMap(fieldMap);
    }

    public static <T> MathExp<T> min(TableFieldFunc<T,?> field){
        return min(field,null);
    }

    public static <T> MathExp<T> max(TableFieldFunc<T,?> field,Map<String,String> fieldMap){
        return new MathExp<T>(MathType.MAX, field) {
            @Override
            public String generateExpression(GenerateParam param) {
                String fieldName=MathExp.getRealFieldName(field,this.getFieldMap());
                if (Objects.nonNull(getAlisa())&& !StringUtils.isEmpty(getAlisa())) {
                    return String.format("max(%s) %s", fieldName, getAlisa());
                }
                else
                {
                    return String.format("max(%s) %s", fieldName, fieldName);
                }
            }
        }.setFieldMap(fieldMap);
    }

    public static <T> MathExp<T> max(TableFieldFunc<T,?> field){
        return max(field,null);
    }

}
